package top.yudoge.dogeeditorserverj.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import top.yudoge.dogeeditorserverj.common.BizException;
import top.yudoge.dogeeditorserverj.common.ErrorCode;
import top.yudoge.dogeeditorserverj.db.*;
import top.yudoge.dogeeditorserverj.db.config.SQLDataSource;
import top.yudoge.dogeeditorserverj.stores.Cache;

import java.util.List;
import java.util.Objects;

/**
 * 数据库元数据Repository
 *
 * key list：
 * ds.数据源名称 -> [所有数据库名列表]
 * ds.数据源名称.数据库名称 -> [所有schema列表]
 * ds.数据源名称.数据库名称.schema名称 -> [所有表名列表]
 * ds.数据源名称.数据库名称.schema名称.表名称.columns -> [所有列信息列表]
 * ds.数据源名称.数据库名称.schema名称.表名称.index ->   [所有索引信息]
 */
@Repository
public class DataBaseMetaDataRepository {
    @Autowired
    private Cache cache;

    @Autowired
    private SQLDataSourceConnectorHolder connectorHolder;


    public void refresh(String dsName) {
        cache.invalidate(dbKey(dsName));
        List<String> database = getDatabase(dsName);
        for (String dbName : database) {
            refresh(dsName, dbName);
        }
    }

    public void refresh(String dsName, String dbName) {
        cache.invalidate(schemaKey(dsName, dbName));
        List<String> schema = getSchema(dsName, dbName);
        for (String schemaName : schema) {
            refresh(dsName, dbName, schemaName);
        }
    }

    public void refresh(String dsName, String dbName, String schemaName) {
        cache.invalidate(tableKey(dsName, dbName, schemaName));
        List<Table> tables = getTable(dsName, dbName, schemaName);
        for (Table table : tables) {
            refresh(dsName, dbName, schemaName, table.getTableName());
        }
    }

    public void refresh(String dsName, String dbName, String schemaName, String tableName) {
        cache.invalidate(columnKey(dsName, dbName, schemaName, tableName));
        cache.invalidate(indexKey(dsName, dbName, schemaName, tableName));
    }

    public List<SQLDataSource> getDataSources() {
        return connectorHolder.getConnectors().stream().map(it -> it.getDataSource()).toList();
    }

    public List<String> getDatabase(String dsName) {
        String key = dbKey(dsName);
        List<String> list = cache.get(key, List.class);
        if (Objects.isNull(list)) {
            SQLDataSourceConnector dsConn =
                    connectorHolder.getConnector(dsName).orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "数据源" + dsName + "不存在"));
            List<String> dsData = dsConn.getDatabases().stream().map(it -> it.getName()).toList();
            cache.put(key, dsData);
            return getDatabase(dsName);
        }
        return list;
    }

    public List<String> getSchema(String dsName, String dbName) {
        String key = schemaKey(dsName, dbName);
        List<String> list = cache.get(key, List.class);
        if (Objects.isNull(list)) {
            SQLDataSourceConnector dsConn =
                    connectorHolder.getConnector(dsName).orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "db" + key + "不存在"));
            List<String> dsData = dsConn.getSchemas(dbName).stream().map(it -> it.getName()).toList();
            cache.put(key, dsData);
            return getSchema(dsName, dbName);
        }
        return list;
    }

    public List<Table> getTable(String dsName, String dbName, String schemaName) {
        String key = tableKey(dsName, dbName, schemaName);
        List<Table> list = cache.get(key, List.class);
        if (Objects.isNull(list)) {
            SQLDataSourceConnector dsConn =
                    connectorHolder.getConnector(dsName).orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "schema" + key + "不存在"));
            List<Table> dsData = dsConn.getTables(dbName, schemaName).stream().toList();
            cache.put(key, dsData);
            return getTable(dsName, dbName, schemaName);
        }
        return list;
    }

    public List<Column> getColumns(String dsName, String dbName, String schemaName, String tableName) {
        String key = columnKey(dsName, dbName, schemaName, tableName);
        List<Column> list = cache.get(key, List.class);
        if (Objects.isNull(list)) {
            SQLDataSourceConnector dsConn =
                    connectorHolder.getConnector(dsName).orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "表" + key + "不存在"));
            List<Column> dsData = dsConn.getColumns(dbName, schemaName, tableName).stream().toList();
            cache.put(key, dsData);
            return getColumns(dsName, dbName, schemaName, tableName);
        }
        return list;
    }

    public List<Index> getIndex(String dsName, String dbName, String schemaName, String tableName) {
        String key = indexKey(dsName, dbName, schemaName, tableName);
        List<Index> list = cache.get(key, List.class);
        if (Objects.isNull(list)) {
            SQLDataSourceConnector dsConn =
                    connectorHolder.getConnector(dsName).orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "表" + key + "不存在"));
            List<Index> dsData = dsConn.getIndices(dbName, schemaName, tableName).stream().toList();
            cache.put(key, dsData);
            return getIndex(dsName, dbName, schemaName, tableName);
        }
        return list;
    }

    private String dsKey() {
        return "ds";
    }

    private String dbKey(String dsName) {
        return String.format("ds.%s", dsName);
    }

    private String schemaKey(String dsName, String dbName) {
        return String.format("ds.%s.%s", dsName, dbName);
    }

    private String tableKey(String dsName, String dbName, String schemaName) {
        return String.format("ds.%s.%s.%s", dsName, dbName, schemaName);
    }

    private String columnKey(String dsName, String dbName, String schemaName, String tableName) {
        return String.format("ds.%s.%s.%s.%s.columns", dsName, dbName, schemaName, tableName);
    }

    private String indexKey(String dsName, String dbName, String schemaName, String tableName) {
        return String.format("ds.%s.%s.%s.%s.index", dsName, dbName, schemaName, tableName);
    }

}
